/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * 
 * @format
 */

/**
 * This transform strips Flow types that are not supported past Babel 7.
 *
 * It is expected that all transforms create valid ESTree AST output. If
 * the transform requires outputting Babel specific AST nodes then it
 * should live in `ConvertESTreeToBabel.js`
 */
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.transformProgram = transformProgram;

var _SimpleTransform = require("../transform/SimpleTransform");

var _createSyntaxError = require("../utils/createSyntaxError");

const nodeWith = _SimpleTransform.SimpleTransform.nodeWith; // Rely on the mapper to fix up parent relationships.

const EMPTY_PARENT = null;

function createSimpleGenericTypeAnnotation(name, nodeForLoc) {
  return {
    type: 'GenericTypeAnnotation',
    id: {
      type: 'Identifier',
      name,
      optional: false,
      typeAnnotation: null,
      loc: nodeForLoc.loc,
      range: nodeForLoc.range,
      parent: EMPTY_PARENT
    },
    typeParameters: null,
    loc: nodeForLoc.loc,
    range: nodeForLoc.range,
    parent: nodeForLoc.parent
  };
}

function createAnyTypeAnnotation(node) {
  return {
    type: 'AnyTypeAnnotation',
    loc: node.loc,
    range: node.range,
    parent: node.parent
  };
}
/**
 * Convert DeclareEnum nodes to DeclareVariable
 */


function mapDeclareEnum(node) {
  return {
    type: 'DeclareVariable',
    kind: 'const',
    id: nodeWith(node.id, {
      typeAnnotation: {
        type: 'TypeAnnotation',
        typeAnnotation: createAnyTypeAnnotation(node.body),
        loc: node.body.loc,
        range: node.body.range,
        parent: EMPTY_PARENT
      }
    }),
    loc: node.loc,
    range: node.range,
    parent: node.parent
  };
}
/**
 * Remove `this` param from functions.
 */


function mapFunction(node) {
  // Remove the first parameter if it is a this-type annotation,
  // which is not recognized by Babel.
  if (node.params.length !== 0 && node.params[0].name === 'this') {
    return nodeWith(node, {
      params: node.params.slice(1)
    });
  }

  return node;
}
/**
 * Convert to QualifiedTypeIdentifier
 */


function mapQualifiedTypeofIdentifier(node) {
  return {
    type: 'QualifiedTypeIdentifier',
    qualification: node.qualification.type === 'QualifiedTypeofIdentifier' ? mapQualifiedTypeofIdentifier(node.qualification) : node.qualification,
    id: node.id,
    loc: node.loc,
    range: node.range,
    parent: node.parent
  };
}

function transformProgram(program, _options) {
  return _SimpleTransform.SimpleTransform.transformProgram(program, {
    transform(node) {
      switch (node.type) {
        case 'SymbolTypeAnnotation':
          {
            // Convert to simple generic type annotation
            return createSimpleGenericTypeAnnotation('symbol', node);
          }

        case 'BigIntTypeAnnotation':
          {
            // Convert to simple generic type annotation
            return createSimpleGenericTypeAnnotation('bigint', node);
          }

        case 'ObjectTypeAnnotation':
          {
            const shouldStrip = node.properties.some(prop => prop.type === 'ObjectTypeMappedTypeProperty');

            if (shouldStrip) {
              return createAnyTypeAnnotation(node);
            }

            return node;
          }

        case 'ObjectTypeMappedTypeProperty':
          {
            throw (0, _createSyntaxError.createSyntaxError)(node, `Invalid AST structure, ObjectTypeMappedTypeProperty found outside of an ObjectTypeAnnotation`);
          }

        case 'IndexedAccessType':
        case 'OptionalIndexedAccessType':
        case 'KeyofTypeAnnotation':
        case 'ConditionalTypeAnnotation':
        case 'InferTypeAnnotation':
        case 'TupleTypeLabeledElement':
        case 'TupleTypeSpreadElement':
        case 'ComponentTypeAnnotation':
        case 'TypeOperator':
        case 'TypePredicate':
          {
            // Babel does not support these generic types, so convert to any
            return createAnyTypeAnnotation(node);
          }

        case 'QualifiedTypeofIdentifier':
          {
            return mapQualifiedTypeofIdentifier(node);
          }

        case 'DeclareEnum':
          {
            return mapDeclareEnum(node);
          }

        case 'FunctionDeclaration':
        case 'FunctionExpression':
          {
            return mapFunction(node);
          }

        default:
          {
            return node;
          }
      }
    }

  });
}